const express = require('express')
const app = express()
const fs = require('fs')
const path = require('path')
const sparkMD5 = require('spark-md5')
const router = express.Router()
// 图片上传解析
const multiparty = require('multiparty')
// 存储地址
const uploadDir = `${__dirname}/upload`

// 端口号
const PORT = 3000

app.use(express.urlencoded({
  extended: false,
  limit: '1024mb'
}))

// 判断是否有当前文件夹
function ruleDirFile(file) {
  fs.access(file, fs.constants.F_OK, (err) => {
    if (err) {
      fs.mkdirSync(file)
    }
  })
}

// 解析
function handleMultiparty(req, res, temp) {
  return new Promise((resolve, reject) => {
    const form = new multiparty.Form({
      maxFieldsSize: 200 * 1024 * 1024, //最大上传大小, 200mb
      // 图片存放地址
      uploadDir: !temp ? uploadDir : null
    });
    // multiparty解析
    form.parse(req, function (err, fields, files) {
      // 有错误返回错误
      if (err) {
        res.status(400).json({
          code: 400,
          msg: err
        })
        reject(err);
        return;
      }
      // 返回成功数据
      resolve({
        fields,
        files
      });
    })
  })
}

// 基于FORM-DATA上传数据
router.post('/single1', async (req, res) => {
  const {
    fields,
    files
  } = await handleMultiparty(req, res)
  const file = files.file[0]
  res.status(200).json({
    code: 200,
    fileName: file.originalFilename,
    path: `http://127.0.0.1:${PORT}/upload/` + path.basename(file.path)
    // path: file.path.replace(__dirname, `http://127.0.0.1:${PORT}`)
  })
})

// 上传BASE64
router.post('/single2', (req, res) => {
  let {
    chunk,
    filename
  } = req.body
  chunk = decodeURIComponent(chunk) // 解码
  chunk = chunk.replace(/^data:image\/\w+;base64,/, ""); // 去除base64的头部
  chunk = Buffer.from(chunk, "base64") // 把base64转换为buffer
  const spark = new sparkMD5.ArrayBuffer() // 通过文件内容生成hash值，基于文件内容生成hash值，并且是根据文件内容生成hash值，不会重复生成
  spark.append(chunk) // 把buffer放入spark里面
  const name = /\.([0-9a-zA-Z]+)$/.exec(filename)[1] // 获取图片后缀名 jpg/png
  const pathName = `${uploadDir}/${spark.end()}.${name}`;
  // 判断图片是否存在，存在就直接返回，不存在就写入
  fs.access(`${uploadDir}/${pathName}`, fs.constants.F_OK, (err) => {
    if (err) {
      fs.writeFileSync(pathName, chunk);
    }
    res.status(200).json({
      code: 200,
      originalFilename: filename,
      path: pathName.replace(__dirname, `http://127.0.0.1:${PORT}`)
    })
  })

})

// 切片上传 && 合并
router.post('/single3', async (req, res) => {
  const {
    fields,
    files
  } = await handleMultiparty(req, res,true)
  const [chunk] = files.chunk
  const [filename] = fields.filename
  const name = /([0-9a-zA-Z]+)_\d+/.exec(filename)[1]
  let address = `${uploadDir}/${name}`
  // 查看有当前文件夹，没有创建文件夹及
  !fs.existsSync(address) ? fs.mkdirSync(address) : null
  // 把所有切片文件放入此文件夹下
  address = `${address}/${filename}`
  // 判断当前切片是否已经上传了
  fs.access(address, async err => {
    if (!err) {
      // 存在就直接返回
      res.status(200).json({
        code: 200,
        path: address.replace(__dirname, `http://127.0.0.1:${PORT}`)
      })
      return
    }
    // 为了测试出效果，延迟1秒钟
    await new Promise(resolve => {
      setTimeout(_ => {
        resolve()
      }, 200)
    })

    /** 
     * !不存在的再创建 
     * */
    // 创建可读流,读取指定文件
    const readStream = fs.createReadStream(chunk.path)
    // 创建可写流，写入一个字符串或对象
    const writeStream = fs.createWriteStream(address)
    // 将两个数据流连接起来
    readStream.pipe(writeStream)
    // 完成
    readStream.on('end', function() {
      // 同步删除文件
      fs.unlinkSync(chunk.path)
      res.status(200).json({
        code: 200,
        path: address.replace(__dirname, `http://127.0.0.1:${PORT}`)
      })
    })
    
  })
})

router.get('/merge', (req, res) => {
  let { hash } = req.query
  // 获取文件对应地址
  const pathName = `${uploadDir}/${hash}`
  // 读取文件
  const fileList = fs.readdirSync(pathName)
  let suffix
  // 排列顺序
  fileList.sort((a, b) => {
      let reg = /_(\d+)/
      return reg.exec(a)[1] - reg.exec(b)[1]
  }).forEach(item => {
      // 检查是否有文件后缀名，没有获取后缀名
      !suffix ? suffix = /\.([0-9a-zA-Z]+)$/.exec(item)[1] : null
      // 同步地将数据追加到文件中，如果文件尚不存在则创建该文件   ↓同步读取文件
      fs.appendFileSync(`${uploadDir}/${hash}.${suffix}`, fs.readFileSync(`${pathName}/${item}`))
      // 删除当前文件夹下的信息
      fs.unlinkSync(`${pathName}/${item}`);
  })
  // 同步删除目录
  fs.rmdirSync(pathName)
  res.status(200).json({
    code: 200,
    path: `http://127.0.0.1:${PORT}/upload/${hash}.${suffix}`
  })
})

// 测试
router.get('/test', (req, res) => {
  res.status(200).json({
    code: 200,
    msg: '运行成功'
  })
})
// 拦截错误
router.use((err, req, res, next) => {
  res.status(400).json({
    code: 400,
    msg: err
  })
})

app.use('/api', router)
// 加载静态文件
app.use(express.static('./'))

app.listen(PORT, () => {
  console.log(`启动成功，端口号${PORT}`);
})